﻿
namespace ExtensionMethods.Test.Extensions
{
    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Groundwork.Extensions;
    using FluentAssertions;
    using System.IO;
    using System.Security.Cryptography;
    using System.Collections.Generic;
    using System.Xml;
    using System.Xml.Linq;

    [TestClass]
    public class StringsTests
    {
        /// <summary>
        /// Tests if each character is passed to the action delegate.
        /// </summary>
        [TestMethod]
        public void StringCharacterForEach()
        {
            int counter = 0;
            string testing = "test";
            testing.ForEach(
                (o) =>
                {
                    counter++;
                });

            counter.Should().Be(4);
        }

        /// <summary>
        /// Tests if the string values are passed to the action delegate.
        /// </summary>
        [TestMethod]
        public void StringArrayForEach()
        {
            int counter = 0;
            string[] testing =
            {
                string.Empty, string.Empty, string.Empty
            };

            testing.ForEach((o) =>
            {
                counter++;
            });

            counter.Should().Be(3);
        }

        /// <summary>
        /// Tests if a null is thrown.
        /// </summary>
        [TestMethod]
        public void StringTestIsNull()
        {
            string testing = null;
            try
            {
                testing.ThrowIfNullOrEmpty("string was null");
            }
            catch (Exception exception)
            {
                exception.Should().BeOfType<ArgumentNullException>();
                exception.Message.Should().Be("context: , message: string was null\r\nParameter name: value");
            }
        }


        /// <summary>
        /// Tests if a null is thrown.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(IOException))]
        public void StringToFile()
        {
            string testing = "Simple string";
            testing.SaveToFile(@"c:\logging", "testfile.txt");
            File.Exists(@"c:\logging\testfile.txt").Should().BeTrue();
        }

        [TestMethod]
        public void ProtectThenUnProtectString()
        {
            string testvalue = "hello world";
            byte[] outValue = testvalue.Protect("testing123", DataProtectionScope.CurrentUser);
            outValue.Length.Should().BePositive();

            string recoveredValue = outValue.UnProtect("testing123", DataProtectionScope.CurrentUser);
            recoveredValue.Should().Be("hello world");
        }

        [TestMethod]
        [ExpectedException(typeof(CryptographicException))]
        public void ProtectThenUnProtectinvalidUnProtectAntropyString()
        {
            string testvalue = "hello world";
            byte[] outValue = testvalue.Protect("testing123", DataProtectionScope.CurrentUser);
            outValue.Length.Should().BePositive();

            string recoveredValue = outValue.UnProtect("failing", DataProtectionScope.CurrentUser);
        }

        [TestMethod]
        public void ProtectThenUnProtectNoAntropyString()
        {
            string testvalue = "hello world";
            byte[] outValue = testvalue.Protect();
            outValue.Length.Should().BePositive();

            string recoveredValue = outValue.UnProtect();
            recoveredValue.Should().Be("hello world");
        }

        [TestMethod]
        public void StringtoDictionaryStringString()
        {
            string list = "A,B;C,D;E,F";
            Dictionary<string, string> convertDictionary = list.ToDictionary<string, string>(',', ';');
            convertDictionary.Count.Should().Be(3);
        }

        [TestMethod]
        public void StringtoDictionaryStringInt()
        {
            string list = "A,1;B,2;C,3";
            Dictionary<string, int> convertDictionary = list.ToDictionary<string, int>(',', ';');
            convertDictionary.Count.Should().Be(3);
            convertDictionary["A"].Should().Be(1);
            convertDictionary["B"].Should().Be(2);
            convertDictionary["C"].Should().Be(3);
        }

        [TestMethod]
        public void StringtoDictionaryIntString()
        {
            string list = "1,A;2,B;3,C";
            Dictionary<int, string> convertDictionary = list.ToDictionary<int, string>(',', ';');
            convertDictionary.Count.Should().Be(3);
            convertDictionary[1].Should().Be("A");
            convertDictionary[2].Should().Be("B");
            convertDictionary[3].Should().Be("C");
        }

        [TestMethod]
        public void StringtoDictionaryStringStringMissingValue()
        {
            string list = "A,;B,D;E,";
            Dictionary<string, string> convertDictionary = list.ToDictionary<string, string>(',', ';');
            convertDictionary.Count.Should().Be(3);
            convertDictionary["A"].Should().Be(string.Empty);
            convertDictionary["B"].Should().Be("D");
            convertDictionary["E"].Should().Be(string.Empty);
        }

        [TestMethod]
        public void StringBindToModel()
        {
            string xml = "<testModel><Item1>hello</Item1><Item2>world</Item2></testModel>";

            testModel resultModel = xml.XmlDeserialize<testModel>();
            resultModel.Should().NotBeNull();
            resultModel.Item1.Should().Be("hello");
            resultModel.Item2.Should().Be("world");
        }

        [TestMethod]
        public void StringUnBindToXml()
        {
            testModel model = new testModel();
            model.Item1 = "hello";
            model.Item2 = "world";
            string outputXml = model.XmlSerialize();
            outputXml.Should().Contain("hello");
        }

        [TestMethod]
        public void StringToXElement()
        {
            string xml = "<testModel><Item1>hello</Item1><Item2>world</Item2></testModel>";
            XDocument doc = xml.ToXDocument();
            doc.ToString().Should().Be("<testModel>\r\n  <Item1>hello</Item1>\r\n  <Item2>world</Item2>\r\n</testModel>");
        }


        [TestMethod]
        public void StringToXmlDocument()
        {
            string xml = "<testModel><Item1>hello</Item1><Item2>world</Item2></testModel>";
            XmlDocument doc = xml.ToXmlDocument();
            doc.OuterXml.Should().Be(xml);
        }
        #region Format
        [TestMethod]
        public void StringFormatTestOneParamter()
        {
            "hello {0}".Format(new Object[] { "world" }).Should().Be("hello world");
        }

        [TestMethod]
        public void StringFormatTestMultipleParamter()
        {
            "hello {0} {1} {2} {3}".Format(new object[] { "world", "value 1", "value 2", "value 3" }).Should().Be("hello world value 1 value 2 value 3");
        }

        [TestMethod]
        public void StringFormatTestParamsProvidedButNoneSpecifiedInFormatStringParamter()
        {
            "hello".Format(new object[] { "world", "value 1", "value 2", "value 3" }).Should().Be("hello");
        }
        #endregion

        #region isNullOrEmpty
        [TestMethod]
        public void StringIsNullOrEmptyPopulatedString()
        {
            string value = "hello";
            value.IsNullOrEmpty().Should().BeFalse();
        }

        [TestMethod]
        public void StringIsNullOrEmptyEmptyString()
        {
            string value = string.Empty;
            value.IsNullOrEmpty().Should().BeTrue();
        }

        [TestMethod]
        public void StringIsNullOrEmptyNullString()
        {
            string value = null;
            value.IsNullOrEmpty().Should().BeTrue();
        }
        #endregion

        #region throwIfNullOrEmpty
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void StringNullOrEmptyThrowArgumentNullException()
        {
            string value = string.Empty;
            value.ThrowIfNullOrEmpty();
        }

        [TestMethod]
        public void StringNullOrEmptyShouldNotThrowArgumentNullException()
        {
            string value = "hello";
            value.ThrowIfNullOrEmpty();
            value.Should().NotBeNullOrEmpty();
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void StringNullOrEmptyThrowNullReferenceException()
        {
            string value = null;
            value.ThrowIfNull();
        }

        [TestMethod]
        public void StringNullOrEmptyShouldNotThrowNullReferenceException()
        {
            string value = "hello";
            value.ThrowIfNull();
            value.Should().NotBeNullOrEmpty();
        }
        #endregion

        #region ThrowIfGreaterThan
        [TestMethod]
        public void GreaterThanStringThrowArgumentOutOfRangeExceptionWithPopulatedString()
        {
            string value = "hello";
            value.ThrowIfGreaterThan(6);
        }

        [TestMethod]
        public void GreaterThanStringShouldNotThrowArgumentOutOfRangeExceptionEmptyString()
        {
            string value = string.Empty;
            value.ThrowIfGreaterThan(6);
        }
        [TestMethod]
        public void GreaterThanStringShouldNotThrowArgumentOutOfRangeExceptionEmptyString2()
        {
            string value = string.Empty;
            value.ThrowIfGreaterThan(0);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void GreaterThanStringShouldThrowArgumentOutOfRangeExceptionEmptyString3()
        {
            string value = string.Empty;
            value.ThrowIfGreaterThan(-1);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void GreaterThanStringShouldThrowArgumentOutOfRangeExceptionWithLargeString()
        {
            string value = "hello world";
            value.ThrowIfGreaterThan(5);
        }
        #endregion

        #region throwIfLessThan
        [TestMethod]
        public void LessThanStringThrowArgumentOutOfRangeExceptionWithPopulatedString()
        {
            string value = "hello";
            value.ThrowIfLessThan(3);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void LessThanStringShouldNotThrowArgumentOutOfRangeExceptionEmptyString()
        {
            string value = string.Empty;
            value.ThrowIfLessThan(6);
        }

        [TestMethod]
        public void LessThanStringShouldNotThrowArgumentOutOfRangeExceptionEmptyString2()
        {
            string value = string.Empty;
            value.ThrowIfLessThan(0);
        }

        [TestMethod]
        public void LessThanStringShouldThrowArgumentOutOfRangeExceptionEmptyString3()
        {
            string value = string.Empty;
            value.ThrowIfLessThan(-1);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void LessThanStringShouldThrowArgumentOutOfRangeExceptionWithLargeString()
        {
            string value = "hello world";
            value.ThrowIfLessThan(20);
        }
        #endregion


        #region Switch
        [TestMethod]
        public void SwitchTestAction()
        {
            string value = string.Empty;
            Dictionary<string, Action<object[]>> proc = new Dictionary<string, Action<object[]>>()
             {
                 {"key1", (o) => { value = (string)o[0]; }},
                 {"key2", (o) => { value = (string)o[0]; }}
             };
            string testing = "key1";
            testing.Switch<string>(proc, "value1");
            value.Should().Be("value1");
        }

        [TestMethod]
        public void SwitchTestFunc()
        {
            string value = string.Empty;
            Dictionary<string, Func<object[], string>> proc = new Dictionary<string, Func<object[], string>>()
             {
                 {"key1", (o) => { return (string)o[0] + " output 1"; }},
                 {"key2", (o) => { return (string)o[0] + " output 2"; }}
             };
            string testing = "key1";
            testing.Switch<string, string>(proc, "value1").Should().Be("value1 output 1");
        }
        #endregion

        [TestMethod]
        public void StringArrayContains()
        {
            string[] testArray = new string[] { "A", "B", "C", "D" };
            testArray.Contains("B").Should().BeTrue();
        }

        [TestMethod]
        public void StringArrayNotContains()
        {
            string[] testArray = new string[] { "A", "B", "C", "D" };
            testArray.Contains("Z").Should().BeFalse();
        }

        [TestMethod]
        public void StringLorumIpsum()
        {
            string testString = "".LorumIpsum(1, 10, 3, 4, 2);
            testString.Should().NotBeNullOrEmpty();
            testString.Length.Should().BeGreaterThan(10);
        }


    }
}
